package com.yami.trading.huobi.hobi.internal;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.yami.trading.bean.data.domain.*;
import com.yami.trading.bean.item.domain.Item;
import com.yami.trading.common.util.Arith;
import com.yami.trading.common.util.StringUtils;
import com.yami.trading.common.util.ThreadUtils;
import com.yami.trading.common.util.UTCDateUtils;
import com.yami.trading.huobi.data.internal.KlineConstant;
import com.yami.trading.huobi.data.internal.KlineService;
import com.yami.trading.huobi.data.model.TimeseriesResult;
import com.yami.trading.huobi.data.websocket.model.market.MarketDepthEvent;
import com.yami.trading.huobi.data.websocket.model.market.MarketTrade;
import com.yami.trading.huobi.data.websocket.model.market.MarketTradeEvent;
import com.yami.trading.huobi.data.websocket.model.market.PriceLevel;
import com.yami.trading.huobi.hobi.Config;
import com.yami.trading.huobi.hobi.HobiDataService;
import com.yami.trading.huobi.hobi.constant.TraderMadeOptions;
import com.yami.trading.huobi.hobi.http.HttpHelper;
import com.yami.trading.huobi.hobi.http.HttpMethodType;
import com.yami.trading.service.item.ItemService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;

@Component
public class HobiDataServiceImpl implements HobiDataService {
    private static Logger logger = LoggerFactory.getLogger(HobiDataServiceImpl.class);

    @Autowired
    private XueQiuDataServiceImpl xueQiuDataService;
    @Autowired
    private XinLangDataServiceImpl xinLangDataService;
    @Autowired
    private ItemService itemService;
    @Autowired
    private KlineService klineService;
    /**
     * 接口调用间隔（毫秒）
     */
    private int interval = 100;

    private int sleep = 100;
    /**
     * 最后一次访问接口时间
     */
    private volatile Date last_time = new Date();

    private volatile boolean lock = false;

    public static void main(String[] args) {
        HobiDataServiceImpl huobi = new HobiDataServiceImpl();
        Map<String, List<Kline>> etcusd = huobi.getDailyWeekMonthHistory("ETCUSD");
        System.out.println(etcusd);
    }

    // 针对虚拟货币的
    @Override
    public List<Realtime> realtime(int maximum) {
        List<Realtime> list = new ArrayList<Realtime>();
        boolean current_lock = false;
        if (lock || (new Date().getTime() - last_time.getTime()) < interval) {
            ThreadUtils.sleep(sleep);
            if (maximum >= 100) {
                return list;
            } else {
                return realtime(++maximum);
            }

        } else {
            try {
                current_lock = true;
                lock = true;
                Map<String, Object> param = new HashMap<String, Object>();
                String result = HttpHelper.getJSONFromHttp(Config.url + Config.tickers, param, HttpMethodType.GET);
                JSONObject resultJson = JSON.parseObject(result);
                String status = resultJson.getString("status");
                if ("ok".equals(status)) {
                    JSONArray dataArray = resultJson.getJSONArray("data");
                    Long ts = resultJson.getLongValue("ts");
                    for (int i = 0; i < dataArray.size(); i++) {
                        JSONObject realtimeJson = dataArray.getJSONObject(i);
                        Realtime realtime = new Realtime();
                        Item item = itemService.findBySymbol(realtimeJson.getString("symbol"));
                        if (item == null) {
                            continue;
                        }
                        realtime.setSymbol(item.getSymbol());
                        realtime.setName(item.getName());
                        realtime.setTs(ts);
                        realtime.setOpen(realtimeJson.getBigDecimal("open"));
                        realtime.setClose(realtimeJson.getBigDecimal("close"));
                        realtime.setHigh(realtimeJson.getBigDecimal("high"));
                        realtime.setLow(realtimeJson.getBigDecimal("low"));
                        realtime.setAmount(realtimeJson.getBigDecimal("amount"));
                        realtime.setVolume(realtimeJson.getBigDecimal("vol"));
                        list.add(realtime);
                    }

                } else {
                    logger.error(" realtime()error, resultJson [ " + resultJson.toJSONString() + " ]");
                }
            } catch (Exception e) {
                logger.error("error", e);
            } finally {
                if (current_lock) {
                    lock = false;
                    last_time = new Date();
                }

            }
        }
        return list;
    }

    @Override
    public List<Kline> kline(String symbol, String period, Integer num, int maximum) {
        List<Kline> list = new ArrayList<Kline>();
        Item item = itemService.findBySymbol(symbol);
        if (item == null) {
            return list;
        }
        boolean current_lock = false;
        if (lock || (new Date().getTime() - last_time.getTime()) < interval) {
            ThreadUtils.sleep(sleep);
            if (maximum >= 100) {
                return list;
            } else {
                return this.kline(symbol, period, num, ++maximum);
            }

        } else {
            try {
                current_lock = true;
                lock = true;
                Map<String, Object> param = new HashMap<String, Object>();
                param.put("symbol", symbol);
                param.put("period", period);
                if (num == null) {
                    if (Kline.PERIOD_1MIN.equals(period)) {
                        param.put("size", 1440);
                    }
                    if (Kline.PERIOD_5MIN.equals(period)) {
                        param.put("size", 576);
                    }
                    if (Kline.PERIOD_15MIN.equals(period)) {
                        param.put("size", 576);
                    }
                    if (Kline.PERIOD_30MIN.equals(period)) {
                        param.put("size", 576);
                    }
                    if (Kline.PERIOD_60MIN.equals(period)) {
                        param.put("size", 576);
                    }

                    if (Kline.PERIOD_4HOUR.equals(period)) {
                        param.put("size", 576);
                    }
                    if (Kline.PERIOD_1DAY.equals(period)) {
                        param.put("size", 500);
                    }
                    if (Kline.PERIOD_1MON.equals(period)) {
                        param.put("size", 500);
                    }
                    if (Kline.PERIOD_1WEEK.equals(period)) {
                        param.put("size", 500);
                    }

                } else {
                    param.put("size", num);
                }

                String result = HttpHelper.getJSONFromHttp(Config.url + Config.kline, param, HttpMethodType.GET);
                JSONObject resultJson = JSON.parseObject(result);
                String status = resultJson.getString("status");
                if ("ok".equals(status)) {
                    JSONArray dataArray = resultJson.getJSONArray("data");
                    /**
                     * 丢弃第一行数据
                     */
                    int start = 1;
                    if (num != null && num == 1) start = 0;
                    for (int i = start; i < dataArray.size(); i++) {
                        JSONObject realtimeJson = dataArray.getJSONObject(i);
                        Kline kline = new Kline();
                        kline.setSymbol(item.getSymbol());
                        kline.setPeriod(period);
                        kline.setTs(Long.valueOf(realtimeJson.getString("id") + "000"));
                        kline.setOpen(realtimeJson.getBigDecimal("open"));
                        kline.setClose(realtimeJson.getBigDecimal("close"));
                        kline.setHigh(realtimeJson.getBigDecimal("high"));
                        kline.setLow(realtimeJson.getBigDecimal("low"));
                        kline.setVolume(realtimeJson.getBigDecimal("vol"));
                        list.add(kline);
                    }

                }
            } catch (Exception e) {
                logger.error("error", e);
            } finally {
                if (current_lock) {
                    lock = false;
                    last_time = new Date();
                }

            }
        }
        return list;
    }

    /**
     * 市场深度数据（20档）,包装，数据本地化处理
     */
    public Depth depthDecorator(String symbol, int maximum) {
        Depth depth = this.depth(symbol, maximum);
        Item item = itemService.findBySymbol(symbol);
        if ((depth == null || item.getAdjustmentValue() == null || item.getAdjustmentValue().intValue() == 0) && (item.getMultiple().intValue() == 0 || item.getMultiple().intValue() == 1)) {
            return depth;
        }

        List<DepthEntry> asks = depth.getAsks();
        for (int i = 0; i < asks.size(); i++) {
            DepthEntry depthEntry = asks.get(i);

            /**
             * 调整交易量倍数和 行情值
             */
            if (item.getMultiple().doubleValue() > 0) {
                depthEntry.setAmount(Arith.mul(depthEntry.getAmount(), item.getMultiple().doubleValue()));
            } else {
                depthEntry.setAmount(depthEntry.getAmount());
            }
            depthEntry.setPrice(Arith.add(depthEntry.getPrice(), item.getAdjustmentValue().doubleValue()));
        }

        List<DepthEntry> bids = depth.getBids();
        for (int i = 0; i < bids.size(); i++) {
            DepthEntry depthEntry = bids.get(i);
            /**
             * 调整交易量倍数和 行情值
             */
            if (item.getMultiple().doubleValue() > 0) {
                depthEntry.setAmount(Arith.mul(depthEntry.getAmount(), item.getMultiple().doubleValue()));
            } else {
                depthEntry.setAmount(depthEntry.getAmount());
            }
            depthEntry.setPrice(Arith.add(depthEntry.getPrice(), item.getAdjustmentValue().doubleValue()));
        }

        return depth;
    }

    /**
     * 市场深度数据（20档）,包装，数据本地化处理
     */
    public Depth depthDecorator(MarketDepthEvent event, Item item) {
        Depth depth = new Depth();
        depth.setSymbol(item.getSymbol());
        depth.setTs(event.getTs());

        double multiple = item.getMultiple().doubleValue() == 0 ? 1 : item.getMultiple().doubleValue();
        double adjustmentValue = item.getAdjustmentValue() == null ? 0 : item.getMultiple().doubleValue();

        for (PriceLevel priceLevel : event.getDepth().getAsks()) {
            DepthEntry depthEntry = new DepthEntry();
            depthEntry.setPrice(Arith.add(depthEntry.getPrice(), adjustmentValue));
            depthEntry.setAmount(Arith.mul(priceLevel.getAmount().doubleValue(), multiple));
            depth.getAsks().add(depthEntry);
        }

        for (PriceLevel priceLevel : event.getDepth().getBids()) {
            DepthEntry depthEntry = new DepthEntry();
            depthEntry.setPrice(Arith.add(depthEntry.getPrice(), adjustmentValue));
            depthEntry.setAmount(Arith.mul(priceLevel.getAmount().doubleValue(), multiple));
            depth.getBids().add(depthEntry);
        }

        return depth;
    }

    @Override
    public Depth depth(String symbol, int maximum) {
        boolean current_lock = false;
        if (StringUtils.isNullOrEmpty(symbol)) {
            return null;
        }
        if (lock || (new Date().getTime() - last_time.getTime()) < interval) {
            ThreadUtils.sleep(sleep);
            if (maximum >= 100) {
                return null;
            } else {
                return this.depth(symbol, ++maximum);
            }
        } else {
            try {
                current_lock = true;
                lock = true;
                Map<String, Object> param = new HashMap<String, Object>();
                param.put("symbol", symbol);
                param.put("type", "step2");

                String result = HttpHelper.getJSONFromHttp(Config.url + Config.depth, param, HttpMethodType.GET);
                JSONObject resultJson = JSON.parseObject(result);
                String status = resultJson.getString("status");
                if ("ok".equals(status)) {
                    JSONObject dataJson = resultJson.getJSONObject("tick");
                    Long ts = resultJson.getLongValue("ts");
                    Depth depth = new Depth();

                    Item item = itemService.findBySymbol(symbol);
                    if (item == null) {
                        return null;
                    }
                    depth.setSymbol(item.getSymbol());
                    depth.setTs(ts);

                    JSONArray bidsArray = dataJson.getJSONArray("bids");
                    for (int i = 0; i < bidsArray.size(); i++) {

                        JSONArray object = (JSONArray) bidsArray.get(i);
                        DepthEntry depthEntry = new DepthEntry();
                        depthEntry.setPrice(object.getDouble(0));
                        depthEntry.setAmount(object.getDouble(1));
                        depth.getBids().add(depthEntry);

                    }

                    JSONArray asksArray = dataJson.getJSONArray("asks");
                    for (int i = 0; i < asksArray.size(); i++) {
                        JSONArray object = (JSONArray) asksArray.get(i);
                        DepthEntry depthEntry = new DepthEntry();
                        depthEntry.setPrice(object.getDouble(0));
                        depthEntry.setAmount(object.getDouble(1));
                        depth.getAsks().add(depthEntry);

                    }

                    return depth;
                }

            } catch (Exception e) {
                logger.error("error", e);
            } finally {
                if (current_lock) {
                    lock = false;
                    last_time = new Date();
                }

            }
        }
        return null;
    }

    /**
     * 获得近期交易记录,包装，数据本地化处理
     */
    public Trade tradeDecorator(String symbol, int maximum) {
        Trade trade = this.trade(symbol, maximum);
        Item item = itemService.findBySymbol(symbol);
        if ((trade == null || item.getAdjustmentValue() == null || item.getAdjustmentValue().doubleValue() == 0) && (item.getMultiple().doubleValue() == 0 || item.getMultiple().doubleValue() == 1)) {
            return trade;
        }
        List<TradeEntry> data = trade.getData();
        for (int i = 0; i < data.size(); i++) {
            TradeEntry tradeEntry = data.get(i);

            /**
             * 调整交易量倍数和 行情值
             */
            if (item.getMultiple().doubleValue() > 0) {
                tradeEntry.setAmount(Arith.mul(tradeEntry.getAmount(), item.getMultiple().doubleValue()));
            } else {
                tradeEntry.setAmount(tradeEntry.getAmount());
            }
            tradeEntry.setPrice(Arith.add(tradeEntry.getPrice(), item.getAdjustmentValue().doubleValue()));
        }
        return trade;

    }

    /**
     * 获得近期交易记录,包装，数据本地化处理
     */
    public Trade tradeDecorator(MarketTradeEvent event, Item item) {
        Trade trade = new Trade();
        trade.setSymbol(item.getSymbol());
        trade.setTs(event.getTs());
        double multiple = item.getMultiple().doubleValue() == 0 ? 1 : item.getMultiple().doubleValue();
        double adjustmentValue = item.getAdjustmentValue() == null ? 0 : item.getMultiple().doubleValue();
        for (MarketTrade value : event.getList()) {
            TradeEntry entry = new TradeEntry();
            entry.setAmount(Arith.mul(value.getAmount().doubleValue(), multiple));
            entry.setPrice(Arith.add(value.getPrice().doubleValue(), adjustmentValue));
            entry.setTs(value.getTs());
            entry.setPrice(value.getPrice().doubleValue());
            entry.setAmount(value.getAmount().doubleValue());
            entry.setDirection(value.getDirection());
            trade.getData().add(entry);
        }
        return trade;
    }

    @Override
    public Trade trade(String symbol, int maximum) {
        boolean current_lock = false;
        if (StringUtils.isNullOrEmpty(symbol)) {
            return null;
        }
        if (lock || (new Date().getTime() - last_time.getTime()) < interval) {
            ThreadUtils.sleep(sleep);
            if (maximum >= 100) {

                return null;
            } else {
                return this.trade(symbol, ++maximum);
            }
        } else {
            try {
                current_lock = true;
                lock = true;
                Map<String, Object> param = new HashMap<String, Object>();
                param.put("symbol", symbol);

                String result = HttpHelper.getJSONFromHttp(Config.url + Config.trade, param, HttpMethodType.GET);
                JSONObject resultJson = JSON.parseObject(result);
                String status = resultJson.getString("status");
                if ("ok".equals(status)) {
                    JSONObject dataJson = resultJson.getJSONObject("tick");
                    Long ts = resultJson.getLongValue("ts");

                    Trade trade = new Trade();

                    Item item = itemService.findBySymbol(symbol);
                    if (item == null) {
                        return null;
                    }
                    trade.setSymbol(item.getSymbol());
                    trade.setTs(ts);

                    JSONArray dataArray = dataJson.getJSONArray("data");
                    for (int i = 0; i < dataArray.size(); i++) {
                        JSONObject object = dataArray.getJSONObject(i);
                        TradeEntry tradeEntry = new TradeEntry();
                        tradeEntry.setTs(object.getLong("ts"));
                        tradeEntry.setPrice(object.getDouble("price"));
                        tradeEntry.setAmount(object.getDouble("amount"));
                        tradeEntry.setDirection(object.getString("direction"));
                        trade.getData().add(tradeEntry);

                    }
                    return trade;
                }

            } catch (Exception e) {
                logger.error("error", e);
            } finally {
                if (current_lock) {
                    lock = false;
                    last_time = new Date();
                }

            }
        }
        return null;
    }

    @Override
    public List<Symbols> symbols() {
        List<Symbols> list = new ArrayList<Symbols>();
        boolean current_lock = false;
        if (lock || (new Date().getTime() - last_time.getTime()) < interval) {

            return list;
        } else {
            try {
                current_lock = true;
                lock = true;
                Map<String, Object> param = new HashMap<String, Object>();
                String result = HttpHelper.getJSONFromHttp(Config.url + Config.symbols, param, HttpMethodType.GET);
                JSONObject resultJson = JSON.parseObject(result);
                String status = resultJson.getString("status");
                if ("ok".equals(status)) {
                    JSONArray dataArray = resultJson.getJSONArray("data");
                    for (int i = 0; i < dataArray.size(); i++) {
                        JSONObject realtimeJson = dataArray.getJSONObject(i);
                        Symbols symbols = new Symbols();
                        symbols.setBaseCurrency(realtimeJson.getString("base-currency"));
                        symbols.setQuoteCurrency(realtimeJson.getString("quote-currency"));
                        symbols.setLeverageRatio(realtimeJson.getBigDecimal("leverage-ratio"));
                        symbols.setPricePrecision(realtimeJson.getIntValue("price-precision"));
                        symbols.setState(realtimeJson.getString("state"));
                        symbols.setSymbol(realtimeJson.getString("symbol"));
                        list.add(symbols);
                    }

                } else {
                    logger.error(" symbols()error, resultJson [ " + resultJson.toJSONString() + " ]");
                }
            } catch (Exception e) {
                logger.error("error", e);
            } finally {
                if (current_lock) {
                    lock = false;
                    last_time = new Date();
                }

            }
        }
        return list;
    }

    public void setItemService(ItemService itemService) {
        this.itemService = itemService;
    }

    public boolean isXueQiu(String symbol) {
        String type = itemService.findBySymbol(symbol).getType();
        return type.contains("stock") || (type.equalsIgnoreCase(Item.indices));
    }

    public boolean isXinlang(String symbol) {
        if("XAUUSD,XAGUSD,OIL".contains(symbol)){
            return false;

        }
        Item bySymbol = itemService.findBySymbol(symbol);
        String type = bySymbol.getType();
        return Item.forex.contains(type) && Item.forex.equals(bySymbol.getCategory());
    }

    public List<Realtime> realtimeSingle(String symbol) {
        if (isXueQiu(symbol)) {
            return xueQiuDataService.realtimeSingle(symbol);
        }
        if (isXinlang(symbol)) {
            return xinLangDataService.realtimeSingle(symbol);
        }
        List<Realtime> list = new ArrayList<Realtime>();
        try {
            Map<String, String> param = new HashMap<>();
            param.put("currency", symbol);
            String result = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.live, param, HttpMethodType.GET);
            JSONObject resultJson = JSON.parseObject(result);
            String code = resultJson.getString("code");
            if ("ok".equals(code)) {
                JSONArray dataArray = resultJson.getJSONArray("data");
                for (int i = 0; i < dataArray.size(); i++) {
                    JSONObject realtimeJson = dataArray.getJSONObject(i);
                    Realtime realtime = new Realtime();
                    String currency = symbol;
                    currency = realtimeJson.getString("currency");
                    int decimal = itemService.getDecimal(currency);
                    realtime.setSymbol(currency);
                    realtime.setName(currency);
                    realtime.setTs(realtimeJson.getLong("timestamp"));
                    realtime.setOpen(realtimeJson.getBigDecimal("open").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setClose(realtimeJson.getBigDecimal("mid").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setHigh(realtimeJson.getBigDecimal("high").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setLow(realtimeJson.getBigDecimal("low").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setAmount(BigDecimal.ZERO);
                    realtime.setVolume(BigDecimal.ZERO);
                    realtime.setAsk(realtimeJson.getBigDecimal("ask").setScale(decimal, RoundingMode.HALF_UP));
                    realtime.setBid(realtimeJson.getBigDecimal("bid").setScale(decimal, RoundingMode.HALF_UP));
                    list.add(realtime);
                }
            } else {
                logger.error(" realtime()error, resultJson [ " + resultJson.toJSONString() + " ]");
            }
        } catch (Exception e) {
            logger.error("error", e);
        }
        return list;
    }

    public List<Realtime> realtime(String symbols) {
        List<Realtime> realtimeList = Collections.synchronizedList(new ArrayList<Realtime>());
        Splitter.on(",").omitEmptyStrings().splitToList(symbols).parallelStream().map(this::realtimeSingle).forEach(realtimeList::addAll);
        return realtimeList;
    }

    @Override
    public List<Realtime> realtimeXueQiu(String symbols) {
        return xueQiuDataService.realtimeSingle(symbols);
    }

    @Override
    public List<Realtime> realtimeXinLang(String symbols) {
        return xinLangDataService.realtimeSingle(symbols);
    }

    /**
     * 1day       历史数据  ： 周期 1年
     * 1week,1mon 历史数据  ： 周期 5年
     * 请求 350次
     */
    public Map<String, List<Kline>> getDailyWeekMonthHistory(String symbol) {
        if (isXueQiu(symbol)) {
            return xueQiuDataService.getDailyWeekMonthHistory(symbol);
        }else if(isXinlang(symbol)){
            return xinLangDataService.getDailyWeekMonthHistory(symbol);

        }
        Map<String, List<Kline>> map = new HashMap<>();
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
        f.setTimeZone(TimeZone.getTimeZone(UTCDateUtils.GMT_TIME_ZONE));
        Date now = new Date();
        // 每次只能取一年的数据，需要遍历5年
        for (int i = 0; i < TraderMadeOptions.weekAndMonthPeriod; i++) {
            String startDate = UTCDateUtils.addYear(now, -1);
            String endDate = f.format(now);
            now = UTCDateUtils.toDate(startDate, "yyyy-MM-dd");

            Map<String, String> param = new HashMap<>();
            param.put("currency", symbol);
            param.put("start_date", startDate);
            param.put("end_date", endDate);
            param.put("format", "records");
            param.put("api_key", TraderMadeOptions.apiKey);
            String json = null;
            try {
                json = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.timeseries, param, HttpMethodType.GET);
            } catch (Exception e) {
                logger.error("采集外汇k线图失败:{} ", param);
                continue;
            }

            JSONObject resultJson = JSON.parseObject(json);
            JSONArray dataArray = resultJson.getJSONArray("quotes");
            if (dataArray.size() > 0) {
                List<TimeseriesResult> list = JSONObject.parseArray(JSONObject.toJSONString(dataArray), TimeseriesResult.class);
                Map<String, List<TimeseriesResult>> monthMap = new LinkedHashMap<>();
                Map<String, List<TimeseriesResult>> weekMap = new LinkedHashMap<>();
                for (TimeseriesResult result : list) {
                    String month = result.getDate().substring(0, 7);
                    if (monthMap.containsKey(month)) {
                        monthMap.get(month).add(result);
                    } else {
                        List<TimeseriesResult> monthList = new ArrayList<>();
                        monthList.add(result);
                        monthMap.put(month, monthList);
                    }

                    String firstDateOfWeek = UTCDateUtils.getFirstDateOfWeek(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd"));
                    if (weekMap.containsKey(firstDateOfWeek)) {
                        weekMap.get(firstDateOfWeek).add(result);
                    } else {
                        List<TimeseriesResult> weekList = new ArrayList<>();
                        weekList.add(result);
                        weekMap.put(firstDateOfWeek, weekList);
                    }
                }

                map.put(Kline.PERIOD_1WEEK, buildOneWeekPeriod(weekMap, symbol));
                map.put(Kline.PERIOD_1MON, buildOneMonthPeriod(monthMap, symbol));

                // 1day历史数据  ： 周期 1年
                if (0 == i) {
                    map.put(Kline.PERIOD_1DAY, buildOneDayPeriod(list, symbol));
                }
            }
        }
        try {
            List<Kline> dayList = map.get(Kline.PERIOD_1DAY);
            if (CollectionUtil.isNotEmpty(dayList)) {
                map.put(Kline.PERIOD_5DAY, klineService.calculateKline(symbol, 5, Kline.PERIOD_5DAY, dayList));
            }
            List<Kline> montList = map.get(Kline.PERIOD_1MON);

            if (CollectionUtil.isNotEmpty(montList)) {
                map.put(Kline.PERIOD_QUARTER, klineService.calculateKline(symbol, 3, Kline.PERIOD_1MON, montList));
            }

        } catch (Exception e) {
            logger.error("外汇计算 5日和季度k线图失败", e);
        }


        return map;
    }


    /**
     * 获取分钟数据
     */
    public Map<String, List<Kline>> getHourlyAndMinuteHistory(String symbol) {
        Map<String, List<Kline>> map = new HashMap<>();

        List<Kline> fourHourlyList = getTimeseriesForFourHourly(symbol);
        map.put(KlineConstant.PERIOD_4HOUR, fourHourlyList);

        List<Kline> oneHourlyList = getTimeseriesForOneHourly(symbol);
        map.put(KlineConstant.PERIOD_60MIN, oneHourlyList);

        List<Kline> twoHourlyList = getTimeseriesForTwoHourly(symbol);
        if (CollectionUtil.isEmpty(twoHourlyList)) {
            try {
                List<Kline> hourList = map.get(Kline.PERIOD_60MIN);
                if (CollectionUtil.isNotEmpty(hourList)) {
                    map.put(Kline.PERIOD_2HOUR, klineService.calculateKline(symbol, 2, Kline.PERIOD_2HOUR, hourList));
                }
            } catch (Exception e) {
                logger.error("外汇计算 120mink线图失败", e);
            }
        } else {
            map.put(KlineConstant.PERIOD_2HOUR, twoHourlyList);

        }


        List<Kline> thirtyMinuteList = getTimeseriesThirtyMinute(symbol);
        map.put(KlineConstant.PERIOD_30MIN, thirtyMinuteList);

        List<Kline> fifteenMinuteList = getTimeseriesFifteenMinute(symbol);
        map.put(KlineConstant.PERIOD_15MIN, fifteenMinuteList);

        List<Kline> fiveMinuteList = getTimeseriesFiveMinute(symbol);
        map.put(KlineConstant.PERIOD_5MIN, fiveMinuteList);

        List<Kline> oneMinuteList = getTimeseriesOneMinute(symbol);
        map.put(KlineConstant.PERIOD_1MIN, oneMinuteList);
        return map;
    }

    public List<Kline> buildOneDayPeriod(List<TimeseriesResult> list, String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.buildOneDayPeriod(currency);
        }if (isXinlang(currency)) {
            return xinLangDataService.buildOneDayPeriod(currency);
        }
        List<Kline> resList = new ArrayList<>();
        for (TimeseriesResult result : list) {
            Kline kline = new Kline();
            kline.setSymbol(currency);
            kline.setPeriod(Kline.PERIOD_1DAY);
            kline.setTs(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd").getTime());
            kline.setOpen(result.getOpen());
            kline.setClose(result.getClose());
            kline.setHigh(result.getHigh());
            kline.setLow(result.getLow());
            kline.setVolume(BigDecimal.ZERO);
            resList.add(kline);
        }
        return resList;
    }



    public List<Kline> buildOneWeekPeriod(Map<String, List<TimeseriesResult>> weekMap, String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.buildOneWeekPeriod(currency);
        }

        List<Kline> resList = new ArrayList<>();
        for (List<TimeseriesResult> list : weekMap.values()) {
            BigDecimal high = list.stream().map(TimeseriesResult::getHigh).filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            BigDecimal low = list.stream().map(TimeseriesResult::getLow).filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            Kline kline = new Kline();
            kline.setSymbol(currency);
            kline.setPeriod(Kline.PERIOD_1WEEK);
            // 毫秒
            kline.setTs(UTCDateUtils.toDate(list.get(list.size() - 1).getDate()).getTime());
            kline.setOpen(list.get(0).getOpen());
            kline.setClose(list.get(list.size() - 1).getClose());
            kline.setHigh(high);
            kline.setLow(low);
            kline.setVolume(BigDecimal.ZERO);
            resList.add(kline);
        }
        return resList;
    }

    public List<Kline> buildOneMonthPeriod(Map<String, List<TimeseriesResult>> monthMap, String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.buildOneMonthPeriod(currency);
        }
        List<Kline> resList = new ArrayList<>();
        for (List<TimeseriesResult> list : monthMap.values()) {
            BigDecimal high = list.stream().map(TimeseriesResult::getHigh).filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            BigDecimal low = list.stream().map(TimeseriesResult::getLow).filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            Kline kline = new Kline();
            kline.setSymbol(currency);
            kline.setPeriod(Kline.PERIOD_1MON);
            // 毫秒
            kline.setTs(UTCDateUtils.toDate(list.get(list.size() - 1).getDate()).getTime());
            kline.setOpen(list.get(0).getOpen());
            kline.setClose(list.get(list.size() - 1).getClose());
            kline.setHigh(high);
            kline.setLow(low);
            kline.setVolume(BigDecimal.ZERO);
            resList.add(kline);
        }
        return resList;
    }

    public List<Kline> buildQuarterPeriod(Map<String, List<TimeseriesResult>> quarterMap, String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.buildOneMonthPeriod(currency);
        }
        List<Kline> resList = new ArrayList<>();
        for (List<TimeseriesResult> list : quarterMap.values()) {
            BigDecimal high = list.stream().map(TimeseriesResult::getHigh).filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            BigDecimal low = list.stream().map(TimeseriesResult::getLow).filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            Kline kline = new Kline();
            kline.setSymbol(currency);
            kline.setPeriod(Kline.PERIOD_QUARTER);
            // 毫秒
            kline.setTs(UTCDateUtils.toDate(list.get(list.size() - 1).getDate()).getTime());
            kline.setOpen(list.get(0).getOpen());
            kline.setClose(list.get(list.size() - 1).getClose());
            kline.setHigh(high);
            kline.setLow(low);
            kline.setVolume(BigDecimal.ZERO);
            resList.add(kline);
        }
        return resList;
    }

    public List<Kline> buildYearPeriod(Map<String, List<TimeseriesResult>> yearMap, String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.buildOneMonthPeriod(currency);
        }
        List<Kline> resList = new ArrayList<>();
        for (List<TimeseriesResult> list : yearMap.values()) {
            BigDecimal high = list.stream().map(TimeseriesResult::getHigh).filter(Objects::nonNull).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            BigDecimal low = list.stream().map(TimeseriesResult::getLow).filter(Objects::nonNull).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
            Kline kline = new Kline();
            kline.setSymbol(currency);
            kline.setPeriod(Kline.PERIOD_YEAR);
            // 毫秒
            kline.setTs(UTCDateUtils.toDate(list.get(list.size() - 1).getDate()).getTime());
            kline.setOpen(list.get(0).getOpen());
            kline.setClose(list.get(list.size() - 1).getClose());
            kline.setHigh(high);
            kline.setLow(low);
            kline.setVolume(BigDecimal.ZERO);
            resList.add(kline);
        }
        return resList;
    }

    /**
     * Hourly
     * 4hourly 3月
     */
    public List<Kline> getTimeseriesForFourHourly(String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.getTimeseriesForFourHourly(currency);
        } else if (isXinlang(currency)) {
            return xinLangDataService.getTimeseriesForFourHourly(currency);

        }
        List<Kline> resList = new ArrayList<>();
        // 每次只能取一个月的数据，需要遍历3个月
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
        f.setTimeZone(TimeZone.getTimeZone(UTCDateUtils.GMT_TIME_ZONE));
        // Date now = UTCDateUtils.toDate(UTCDateUtils.addHour(new Date(), -4), "yyyy-MM-dd-HH:mm");
        Date now = new Date();
        for (int i = 0; i < TraderMadeOptions.fourHourlyPeriod; i++) {
            String startDate = UTCDateUtils.addDay(now, -30);
            String endDate = f.format(now);
            now = UTCDateUtils.toDate(startDate, "yyyy-MM-dd-HH:mm");

            Map<String, String> param = new HashMap<>();
            param.put("currency", currency);
            param.put("start_date", startDate);
            param.put("end_date", endDate);
            param.put("interval", "hourly");
            param.put("period", "4");
            param.put("format", "records");
            param.put("api_key", TraderMadeOptions.apiKey);
            String json = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.timeseries, param, HttpMethodType.GET);
            JSONObject resultJson = JSON.parseObject(json);
            JSONArray dataArray = resultJson.getJSONArray("quotes");
            if (dataArray.size() > 0) {
                List<TimeseriesResult> list = JSONObject.parseArray(JSONObject.toJSONString(dataArray), TimeseriesResult.class);
                for (TimeseriesResult result : list) {
                    Kline kline = new Kline();
                    kline.setSymbol(currency);
                    kline.setPeriod(Kline.PERIOD_4HOUR);
                    // 毫秒
                    kline.setTs(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd HH:mm").getTime());
                    kline.setOpen(result.getOpen());
                    kline.setClose(result.getClose());
                    kline.setHigh(result.getHigh());
                    kline.setLow(result.getLow());
                    kline.setVolume(BigDecimal.ZERO);
                    resList.add(kline);
                }
            }
        }
        return resList;
    }

    /**
     * Hourly
     * 1hourly 1月
     */
    public List<Kline> getTimeseriesForOneHourly(String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.getTimeseriesForOneHourly(currency);
        }
        if (isXinlang(currency)) {
            return xinLangDataService.getTimeseriesForOneHourly(currency);
        }
        List<Kline> resList = new ArrayList<>();
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
        f.setTimeZone(TimeZone.getTimeZone(UTCDateUtils.GMT_TIME_ZONE));
        // Date now = UTCDateUtils.toDate(UTCDateUtils.addHour(new Date(), -1), "yyyy-MM-dd-HH:mm");
        Date now = new Date();
        String startDate = UTCDateUtils.addDay(now, -30);
        String endDate = f.format(now);

        Map<String, String> param = new HashMap<>();
        param.put("currency", currency);
        param.put("start_date", startDate);
        param.put("end_date", endDate);
        param.put("interval", "hourly");
        param.put("period", "1");
        param.put("format", "records");
        param.put("api_key", TraderMadeOptions.apiKey);
        String json = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.timeseries, param, HttpMethodType.GET);
        JSONObject resultJson = JSON.parseObject(json);
        JSONArray dataArray = resultJson.getJSONArray("quotes");
        if (dataArray.size() > 0) {
            List<TimeseriesResult> list = JSONObject.parseArray(JSONObject.toJSONString(dataArray), TimeseriesResult.class);
            for (TimeseriesResult result : list) {
                Kline kline = new Kline();
                kline.setSymbol(currency);
                kline.setPeriod(Kline.PERIOD_60MIN);
                // 毫秒
                kline.setTs(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd HH:mm").getTime());
                kline.setOpen(result.getOpen());
                kline.setClose(result.getClose());
                kline.setHigh(result.getHigh());
                kline.setLow(result.getLow());
                kline.setVolume(BigDecimal.ZERO);
                resList.add(kline);
            }
        }
        return resList;
    }

    @Override
    public List<Kline> getTimeseriesForTwoHourly(String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.getTimeseriesForTwoHourly(currency);
        }
        if (isXinlang(currency)) {
            return xinLangDataService.getTimeseriesForTwoHourly(currency);
        }
        return Lists.newArrayList();
    }

    /**
     * Minute
     * 30minute 10天
     * 15minute 5天
     * 5minute  2天
     * 1minute  1天
     */
    public List<Kline> getTimeseriesOneMinute(String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.getTimeseriesOneMinute(currency);
        }
        if (isXinlang(currency)) {
            return xinLangDataService.getTimeseriesOneMinute(currency);
        }
        List<Kline> resList = new ArrayList<>();
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
        f.setTimeZone(TimeZone.getTimeZone(UTCDateUtils.GMT_TIME_ZONE));
        Date now = new Date();
        String startDate = UTCDateUtils.addDay(now, -1);
        String endDate = f.format(now);

        Map<String, String> param = new HashMap<>();
        param.put("currency", currency);
        param.put("start_date", startDate);
        param.put("end_date", endDate);
        param.put("interval", "minute");
        param.put("period", "1");
        param.put("format", "records");
        param.put("api_key", TraderMadeOptions.apiKey);
        String json = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.timeseries, param, HttpMethodType.GET);
        JSONObject resultJson = JSON.parseObject(json);
        JSONArray dataArray = resultJson.getJSONArray("quotes");
        if (dataArray.size() > 0) {
            List<TimeseriesResult> list = JSONObject.parseArray(JSONObject.toJSONString(dataArray), TimeseriesResult.class);
            for (TimeseriesResult result : list) {
                Kline kline = new Kline();
                kline.setSymbol(currency);
                kline.setPeriod(Kline.PERIOD_1MIN);
                // 毫秒
                kline.setTs(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd HH:mm").getTime());
                kline.setOpen(result.getOpen());
                kline.setClose(result.getClose());
                kline.setHigh(result.getHigh());
                kline.setLow(result.getLow());
                kline.setVolume(BigDecimal.ZERO);
                resList.add(kline);
            }
        }
        return resList;
    }

    /**
     * Minute
     * 30minute 10天
     * 15minute 5天
     * 5minute  2天
     * 1minute  1天
     */
    public List<Kline> getTimeseriesFiveMinute(String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.getTimeseriesFiveMinute(currency);
        }
        if (isXinlang(currency)) {
            return xinLangDataService.getTimeseriesFiveMinute(currency);
        }
        List<Kline> resList = new ArrayList<>();
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
        f.setTimeZone(TimeZone.getTimeZone(UTCDateUtils.GMT_TIME_ZONE));
        Date now = new Date();
        String startDate = UTCDateUtils.addDay(now, -2);
        String endDate = f.format(now);

        Map<String, String> param = new HashMap<>();
        param.put("currency", currency);
        param.put("start_date", startDate);
        param.put("end_date", endDate);
        param.put("interval", "minute");
        param.put("period", "5");
        param.put("format", "records");
        param.put("api_key", TraderMadeOptions.apiKey);
        String json = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.timeseries, param, HttpMethodType.GET);
        JSONObject resultJson = JSON.parseObject(json);
        JSONArray dataArray = resultJson.getJSONArray("quotes");
        if (dataArray.size() > 0) {
            List<TimeseriesResult> list = JSONObject.parseArray(JSONObject.toJSONString(dataArray), TimeseriesResult.class);
            for (TimeseriesResult result : list) {
                Kline kline = new Kline();
                kline.setSymbol(currency);
                kline.setPeriod(Kline.PERIOD_5MIN);
                // 毫秒
                kline.setTs(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd HH:mm").getTime());
                kline.setOpen(result.getOpen());
                kline.setClose(result.getClose());
                kline.setHigh(result.getHigh());
                kline.setLow(result.getLow());
                kline.setVolume(BigDecimal.ZERO);
                resList.add(kline);
            }
        }
        return resList;
    }

    /**
     * Minute
     * 30minute 10天
     * 15minute 5天
     * 5minute  2天
     * 1minute  1天
     */
    public List<Kline> getTimeseriesFifteenMinute(String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.getTimeseriesFifteenMinute(currency);
        }
        if (isXinlang(currency)) {
            return xinLangDataService.getTimeseriesFifteenMinute(currency);
        }
        List<Kline> resList = new ArrayList<>();
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
        f.setTimeZone(TimeZone.getTimeZone(UTCDateUtils.GMT_TIME_ZONE));
        Date now = new Date();
        for (int i = 0; i < TraderMadeOptions.fifteenMinutePeriod; i++) {
            String startDate = UTCDateUtils.addDay(now, -2);
            String endDate = f.format(now);
            now = UTCDateUtils.toDate(startDate, "yyyy-MM-dd-HH:mm");
            Map<String, String> param = new HashMap<>();
            param.put("currency", currency);
            param.put("start_date", startDate);
            param.put("end_date", endDate);
            param.put("interval", "minute");
            param.put("period", "15");
            param.put("format", "records");
            param.put("api_key", TraderMadeOptions.apiKey);
            String json = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.timeseries, param, HttpMethodType.GET);
            JSONObject resultJson = JSON.parseObject(json);
            JSONArray dataArray = resultJson.getJSONArray("quotes");
            if (dataArray.size() > 0) {
                List<TimeseriesResult> list = JSONObject.parseArray(JSONObject.toJSONString(dataArray), TimeseriesResult.class);
                for (TimeseriesResult result : list) {
                    Kline kline = new Kline();
                    kline.setSymbol(currency);
                    kline.setPeriod(Kline.PERIOD_15MIN);
                    // 毫秒
                    kline.setTs(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd HH:mm").getTime());
                    kline.setOpen(result.getOpen());
                    kline.setClose(result.getClose());
                    kline.setHigh(result.getHigh());
                    kline.setLow(result.getLow());
                    kline.setVolume(BigDecimal.ZERO);
                    resList.add(kline);
                }
            }
        }
        return resList;
    }

    /**
     * Minute
     * 30minute 10天
     * 15minute 5天
     * 5minute  2天
     * 1minute  1天
     */
    public List<Kline> getTimeseriesThirtyMinute(String currency) {
        if (isXueQiu(currency)) {
            return xueQiuDataService.getTimeseriesThirtyMinute(currency);
        }
        if (isXinlang(currency)) {
            return xinLangDataService.getTimeseriesThirtyMinute(currency);
        }
        List<Kline> resList = new ArrayList<>();
        SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd-HH:mm");
        f.setTimeZone(TimeZone.getTimeZone(UTCDateUtils.GMT_TIME_ZONE));
        Date now = new Date();
        for (int i = 0; i < TraderMadeOptions.thirtyMinutePeriod; i++) {
            String startDate = UTCDateUtils.addDay(now, -2);
            String endDate = f.format(now);
            now = UTCDateUtils.toDate(startDate, "yyyy-MM-dd-HH:mm");
            Map<String, String> param = new HashMap<>();
            param.put("currency", currency);
            param.put("start_date", startDate);
            param.put("end_date", endDate);
            param.put("interval", "minute");
            param.put("period", "15");
            param.put("format", "records");
            param.put("api_key", TraderMadeOptions.apiKey);
            String json = HttpHelper.getJSONFromHttpNew(TraderMadeOptions.timeseries, param, HttpMethodType.GET);
            JSONObject resultJson = JSON.parseObject(json);
            JSONArray dataArray = resultJson.getJSONArray("quotes");
            if (dataArray.size() > 0) {
                List<TimeseriesResult> list = JSONObject.parseArray(JSONObject.toJSONString(dataArray), TimeseriesResult.class);
                for (TimeseriesResult result : list) {
                    Kline kline = new Kline();
                    kline.setSymbol(currency);
                    kline.setPeriod(Kline.PERIOD_30MIN);
                    // 毫秒
                    kline.setTs(UTCDateUtils.toDate(result.getDate(), "yyyy-MM-dd HH:mm").getTime());
                    kline.setOpen(result.getOpen());
                    kline.setClose(result.getClose());
                    kline.setHigh(result.getHigh());
                    kline.setLow(result.getLow());
                    kline.setVolume(BigDecimal.ZERO);
                    resList.add(kline);
                }
            }
        }
        return resList;
    }
}
